/* 
 * Copyright 2012 Mikaël GUILLEMOT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package server;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;

/**
 * repondeur http minimaliste
 * 
 * @author Mikael GUILLEMOT
 * 
 */
public class MicroServeur {

	private static int PORT = 80;
	private static String message = "hello";
	private boolean log_state = false;
	private ServerSocket listener = null;
	private RandomAccessFile logfile = null;
	private DateFormat timeStamp = null;
	private boolean run = true;

	private class Sh extends Thread {
		public void run() {
			System.out.print("shuting down server...");
			try {
				listener.close();
			} catch (IOException e) {
				closeLog();
				System.exit(-1);
			}
			System.out.println("done");
		}
	}

	private class Session extends Thread {
		private Socket clientSocket;
		private BufferedReader in;
		private DataOutputStream out;
		private byte[] ard;
		private String adresse;

		public Session(Socket client) {
			clientSocket = client;
			try {
				ard = clientSocket.getInetAddress().getAddress();
				adresse = bv(ard[0]) + "." + bv(ard[1]) + "." + bv(ard[2]) + "." + bv(ard[3]);
				in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
				out = new DataOutputStream(clientSocket.getOutputStream());
			} catch (IOException e) {
				log("erreur <" + adresse + "> : " + e.getMessage());
			}
		}

		public void run() {
			try {
				String request = in.readLine();
				StringTokenizer st = new StringTokenizer(request, " ");
				String cmd_get = st.nextToken();
				String cmd_page = st.nextToken();
				if (cmd_get.equals("GET")) {
					if (!cmd_page.equals("/favicon.ico")) {
						// TODO ICI : on recupere la requette apres l'adresse,
						// on log la requette
						log("<" + adresse + ">Reception requette : " + request);
						String e404 = "<html><head><title>microserver 1.0</title></head>"
								+ "<body><p align=\"center\"><br/>" + message + "<br/><br/><b>[" + adresse
								+ "]</b> <p/></body></html>";
						out.writeBytes("HTTP/1.1 200 OK\r\n");
						out.writeBytes("Content-Length: " + e404.length() + "\r\n");
						out.writeBytes("Content-Type: text/html\r\n\r\n");
						out.writeBytes(e404);
						out.flush();
					}
				} else {
					out.writeBytes("400 Bad Request");
				}
				clientSocket.close();
			} catch (Exception e) {
				log("error <" + adresse + "> : " + e.getMessage());
			}
		}
	}

	private void run() {
		try {
			listener = new ServerSocket(PORT);

		} catch (IOException e) {
			System.out.println("Server cannot start : " + e.getMessage());
			e.printStackTrace();
			System.exit(1);
		}
		Runtime.getRuntime().addShutdownHook(new Sh());
		System.out.println("waiting for client...");
		try {
			while (run) {
				Socket client;
				client = listener.accept();
				Session s = new Session(client);
				s.start();
			}
		} catch (IOException e) {
			log("Server stop(" + e.getMessage() + ")");
			closeLog();
		}
	}

	int bv(byte b) {
		int out = b;
		if (b < 0)
			out = out + 256;
		return out;
	}

	private synchronized void log(String msg) {
		System.out.println(msg);
		if (log_state) {
			try {
				StringBuilder tmp = new StringBuilder(timeStamp.format(new Date()));
				tmp.append(msg);
				tmp.append("\n");
				logfile.write(tmp.toString().getBytes());
			} catch (IOException e) {
				System.err.println("log writing error : " + msg);
			}
		}
	}

	private synchronized void closeLog() {
		if (logfile != null) {
			try {
				logfile.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private MicroServeur(boolean log) {
		timeStamp = new SimpleDateFormat("[dd/MM,hh:mm:ss] ");
		if (log)
			try {
				DateFormat dateFormat = new SimpleDateFormat("ddMMyyhhmmss");
				System.out.println("creation de : log_" + dateFormat.format(new Date()) + ".log");
				logfile = new RandomAccessFile(new File("log_" + dateFormat.format(new Date()) + ".log"), "rw");
				log_state = true;
			} catch (IOException e) {
				System.err.println("[ERROR] Logger initialisation failed! Log function disabled!\n");
			}
	}

	public static void main(String[] args) {
		boolean log = false;
		if (args.length > 0)
			try {
				if (args[0].equalsIgnoreCase("log=y"))
					log = true;
				if (args.length > 1)
					PORT = Integer.parseInt(args[1]);
				if (args.length > 2)
					message = args[2];
				if (args.length > 3)
					throw new Exception();
			} catch (Exception e) {
				System.out
						.println("args : [Log=Y/N] [port] [\"html message\"], to use last args, other must be defined.");
				System.exit(2);
			}
		System.out.println("server starting on port " + PORT + ", message is: " + message);
		MicroServeur serveur = new MicroServeur(log);
		serveur.run();
	}
}
